上節在 model.fit 的敘述中,有提到模型在訓練前會檢查是否有對模型與所包含的層執行過build的動作。而其實可以於model.fit 之前加入程式執行此動作,以 model.build 來執行。
from tensorflow.keras.models import Sequential
from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype("float32") / 255
from tensorflow.keras import layers
from tensorflow.keras.models import Model
model = Sequential([
layers.Dense(512, activation="relu"),
layers.Dense(10, activation="softmax")
])
model.build(input_shape=(None, 784))
model.compile(optimizer="rmsprop",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"])
model.fit(train_images, train_labels, epochs=5, batch_size=128)
model.build 會如何運作?
(1)
會先檢查model.build的參數有沒有傳入 input_shape,也就是input的維度,如果沒有則發出例外錯誤要求傳入input_shape。
(2)
執行keras.engine.sequential._build_graph_network_for_inferred_shape 函式,以輸入的 input_shape 維度傳入 keras.engine.input_layer.Input 先建立起一個input layer實體,接著以此input layer為開頭,迭代 keras.engine.sequential.layers 每個被加入的 layer ,串聯起來。以此範例,每層迭代時會呼叫 keras.engine.base_layer.call,再透過
keras.engine.base_layer._functional_construction_call
-> keras.engine.base_layer._keras_tensor_symbolic_call
-> keras.engine.base_layer._infer_output_signature
執行每個 keras.layers.core.dense.Dense 實體的 call ,也就是layer的實體所實作的call函式。每個layer call,都會檢查是否本身layer已經被build過,如果沒有就會進行build的動作。而以本範例layer本身keras.layers.core.dense是執行 keras.layers.core.dense.build ,build一定會傳入接收的input實體,要明白接收的張量維度為何。然後會利用 keras.engine.base_layer.add_weight 函式分別對 keras.layers.core.dense的 kernel(權重) 與 bias(容錯) 屬性設置對應當初宣告keras.layers.core.dense的參數內容進行初始化。
接著將input與kernel權重內積、經過activation function 轉換、加上bias ,產生 output layer 後變成下一個迭代的 input layer , 重複直至最後一層。最後產生的 output layer 將回傳出去。 這邊就是在做層與層的連結,也確保層與層是有相關聯的。
到目前為模型的weights只是初始設定,還會設定 keras.engine.sequential.Sequential.built 屬性為 True表示模型已經build過了。也將keras.engine.sequential.Sequential._graph_initialized 設定為 True 做標記。
(3)
將 一開始的 input layer 與 (2) 所產生的 output layer ,傳入 keras.engine.functional._init_graph_network 函式為model.Sequential 重新初始化 graph network,透過 keras.engine.functional._map_graph_network 進入 keras.engine.functional._build_map 執行 遞迴keras.engine.functional._build_map_helper 將每個layer共同構成的topology之連結找出,之後也會分別指定 keras.engine.sequential.Sequential.input 為 input layer 實體,與 keras.engine.sequential.Sequential.output 為 output layer 實體。
最新的graph network已經設定好。
最後,model.build 可以在 model.fit 前執行,且無論在model.compiler之前或之後都沒關係,仍然是沒問題可以成功建構模型。